package scenic.swing;

import java.applet.Applet;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Window;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import javax.swing.JComponent;
import javax.swing.RepaintManager;

import scenic.SceneContainer;
import scenic.ScenicJFrame;

public class ScenicRepaintManager extends RepaintManager {
	private RepaintManager orig;
	private Set<ScenicJFrame> invalidFrames = new HashSet<ScenicJFrame>();
	private Set<JComponent> dirtyComponents = new HashSet<JComponent>();
	private Map<JComponent, SceneContainer> componentScenes = new WeakHashMap<JComponent, SceneContainer>();
	
	private static ScenicRepaintManager repaintManager;
	
	public ScenicRepaintManager(RepaintManager orig) {
		this.orig = orig;
		repaintManager = this;
	}
	

	@Override
	public Image getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) {
		return getVolatileOffscreenBuffer(c, proposedWidth, proposedHeight);
	}


	@Override
	public Image getVolatileOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) {
		return null;
	}

	private ScenicJFrame getFrame(Component component) {
		while(component != null) {
			if(component instanceof ScenicJFrame)
				return (ScenicJFrame)component;
			component = component.getParent();
		}
		return null;
	}
	
	private void resetDoubleBuffering(Component component) {
		if(component instanceof JComponent)
			((JComponent)component).setDoubleBuffered(true);
		while(component != null) {
			if(component instanceof ScenicJFrame)
				return;
			component = component.getParent();
			if(component instanceof JComponent) {
				JComponent c = (JComponent)component;
				
				if(c.isDoubleBuffered())
					c.setDoubleBuffered(false);
			}
		}
	}
	
	@Override
	public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
//		System.out.println("addDirtyRegion " + c + " ");
		ScenicJFrame frame = getFrame(c);
		
		if(frame == null) {
			return;
		}
		resetDoubleBuffering(c);
//		frame.repaint();
		invalidFrames.add(frame);
		dirtyComponents.add(c);
		orig.addDirtyRegion(c, x, y, w, h);
	}

	public static boolean isDirty(JComponent c) {
		boolean r = repaintManager.dirtyComponents.contains(c);
		
		if(r)
			repaintManager.dirtyComponents.remove(c);
		return r;
	}
	
	public static SceneContainer getScene(JComponent c) {
		SceneContainer scene = repaintManager.componentScenes.get(c);
		
		if(scene == null) {
			scene = new SceneContainer();
			repaintManager.componentScenes.put(c, scene);
		}
		
		return scene;
	}
	
//	@Override
	public void addDirtyRegion(Applet applet, int x, int y, int w, int h) {
		orig.addDirtyRegion(applet, x, y, w, h);
	}


//	@Override
	public void addDirtyRegion(Window window, int x, int y, int w, int h) {
		orig.addDirtyRegion(window, x, y, w, h);
	}


	@Override
	public Rectangle getDirtyRegion(JComponent aComponent) {
		System.out.println("getDirtyRegion " + aComponent);
		return orig.getDirtyRegion(aComponent);
	}


	@Override
	public Dimension getDoubleBufferMaximumSize() {
		return orig.getDoubleBufferMaximumSize();
	}


	@Override
	public boolean isCompletelyDirty(JComponent aComponent) {
		System.out.println("isCompletelyDirty " + aComponent);
		return orig.isCompletelyDirty(aComponent);
	}


	@Override
	public void markCompletelyClean(JComponent aComponent) {
		System.out.println("markCompletelyClean " + aComponent);
		orig.markCompletelyClean(aComponent);
	}


	@Override
	public void markCompletelyDirty(JComponent aComponent) {
		System.out.println("markCompletelyDirty " + aComponent);
		orig.markCompletelyDirty(aComponent);
	}


	@Override
	public void setDoubleBufferingEnabled(boolean aFlag) {
		orig.setDoubleBufferingEnabled(aFlag);
	}


	@Override
	public void setDoubleBufferMaximumSize(Dimension d) {
		orig.setDoubleBufferMaximumSize(d);
	}


	@Override
	public synchronized void addInvalidComponent(JComponent invalidComponent) {
//		System.out.println("addInvalidComponent " + invalidComponent);
		orig.addInvalidComponent(invalidComponent);
	}

	@Override
	public synchronized void removeInvalidComponent(JComponent component) {
//		System.out.println("removeInvalidComponent" + component);
		orig.removeInvalidComponent(component);
	}

	@Override
	public void paintDirtyRegions() {
//		System.out.println("paintDirtyRegions");
		orig.paintDirtyRegions();
		for(ScenicJFrame f : invalidFrames) {
			f.displayRenderTarget();
		}
		dirtyComponents.clear();
		invalidFrames.clear(); 
		dirtyComponents.clear();
	}


	@Override
	public void validateInvalidComponents() {
//		System.out.println("validateInvalidComponents");
		orig.validateInvalidComponents();
	}

    public boolean isDoubleBufferingEnabled() {
    	return false;
    }
}
